package easycloud.ecs.provider.aliyun;

import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.aliyun.openservices.oss.OSSClient;
import com.aliyun.openservices.oss.OSSErrorCode;
import com.aliyun.openservices.oss.OSSException;
import com.aliyun.openservices.oss.model.ListObjectsRequest;
import com.aliyun.openservices.oss.model.OSSObject;
import com.aliyun.openservices.oss.model.ObjectListing;
import com.aliyun.openservices.oss.model.ObjectMetadata;
import com.aliyun.openservices.oss.model.PutObjectResult;

import easycloud.ecs.api.IProvider;
import easycloud.ecs.api.IPutResult;
import easycloud.ecs.api.ProviderException;
import easycloud.ecs.api.model.IStorageObject;
import easycloud.ecs.api.model.IStorageObjectListing;
import easycloud.ecs.api.model.IStorageObjectMetadata;
import easycloud.ecs.config.ProviderConfig;
import easycloud.ecs.config.ProviderConfigUtils;
import easycloud.ecs.config.ProviderStatistics;
import easycloud.ecs.provider.aliyun.model.AliyunStorageObject;
import easycloud.ecs.provider.aliyun.model.AliyunStorageObjectListing;
import easycloud.ecs.provider.aliyun.model.AliyunStorageObjectMetadata;

public class AliyunOSSProvider implements IProvider {

	private ProviderConfig config;

	private ProviderStatistics statistics;

	private static OSSClient adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory
			.getLog(AliyunOSSProvider.class);

	private static String BUCKET_NAME;

	private static final int RETRY_TIMES = 3;

	public AliyunOSSProvider(ProviderConfig config) {
		this.config = config;
		statistics = new ProviderStatistics();
		AliyunOSSProvider.adapter = new OSSClient(this.config.getAccessKey(),
				this.config.getSecretKey());
		AliyunOSSProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			statistics.incNumberOfDelete();
			AliyunOSSProvider.adapter.deleteObject(
					AliyunOSSProvider.BUCKET_NAME, key);
			result = true;
			AliyunOSSProvider.logger.info(getName() + " delete OK: " + key);
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " delete failed", e);
			throw new ProviderException(getName() + " delete failed", e, key);
		}
		return result;
	}

	/**
	 * 
	 * @param object
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getFile(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		OSSObject result = null;
		try {
			statistics.incNumberOfGet();
			result = AliyunOSSProvider.adapter.getObject(
					AliyunOSSProvider.BUCKET_NAME, key);
			AliyunOSSProvider.logger.info(getName() + " getFile OK: " + key);
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " getFile failed", e);
			throw new ProviderException(getName() + " getFile failed", e, key);
		}
		if (result != null) {
			return new AliyunStorageObject(result);
		} else {
			return null;
		}
	}

	@Override
	public String getName() {
		return "AliyunOSS";
	}

	@Override
	public ProviderStatistics getProviderStatistics() {
		return statistics;
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectMetadata head(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectMetadata result = null;
		try {
			statistics.incNumberOfHead();
			result = AliyunOSSProvider.adapter.getObjectMetadata(
					AliyunOSSProvider.BUCKET_NAME, key);
			AliyunOSSProvider.logger.info(getName() + " head OK: " + key);
		} catch (OSSException e) {
			if (OSSErrorCode.NO_SUCH_KEY.equals(e.getErrorCode())) {
				return null;
			}
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e, key);
		}
		if (result != null) {
			return new AliyunStorageObjectMetadata(result);
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param path
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectListing list(String path, String delimiter)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectListing result = null;
		try {
			ListObjectsRequest request = new ListObjectsRequest(
					AliyunOSSProvider.BUCKET_NAME);
			request.setPrefix(path);
			if (delimiter != null) {
				request.setDelimiter(delimiter);
			}
			statistics.incNumberOfList();
			result = AliyunOSSProvider.adapter.listObjects(request);
			AliyunOSSProvider.logger.info(getName() + " list OK: " + path);
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e, path);
		}
		if (result != null) {
			return new AliyunStorageObjectListing(result);
		} else {
			return null;
		}
	}

	@Override
	public List<String> listRoots() throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		List<String> result = null;
		try {
			ListObjectsRequest request = new ListObjectsRequest(
					AliyunOSSProvider.BUCKET_NAME);
			request.setPrefix("");
			request.setDelimiter("/");
			statistics.incNumberOfListRoots();
			ObjectListing objectListing = AliyunOSSProvider.adapter
					.listObjects(request);
			if (objectListing.getCommonPrefixes() != null) {
				result = objectListing.getCommonPrefixes();
			}
			AliyunOSSProvider.logger.info(getName() + " listRoots OK");
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " listRoots failed", e);
			throw new ProviderException(getName() + " listRoots failed", e, "");
		}
		return result;
	}

	/**
	 * Upload file
	 * 
	 * @param file
	 * @param object
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IPutResult putFile(File file, String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		PutObjectResult putResult = null;
		if (file.isFile() && (file.length() > 0)) {
			final ObjectMetadata metadata = new ObjectMetadata();
			metadata.setContentLength(file.length());
			try {
				statistics.incNumberOfPut();
				putResult = AliyunOSSProvider.adapter.putObject(
						AliyunOSSProvider.BUCKET_NAME, key,
						new FileInputStream(file), metadata);
				AliyunOSSProvider.logger
						.info(getName() + " putFile OK: " + key);
			} catch (Exception e) {
				AliyunOSSProvider.logger.warn(getName() + " putFile failed", e);
				throw new ProviderException(getName() + " putFile failed", e,
						key);
			}
		}
		return new AliyunPutResult(putResult);
	}

	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		boolean result = false;
		try {
			if (StringUtils.isEmpty(AliyunOSSProvider.BUCKET_NAME)) {
				AliyunOSSProvider.BUCKET_NAME = UUID.randomUUID().toString();
				config.setBucketName(AliyunOSSProvider.BUCKET_NAME);
				AliyunOSSProvider.adapter
						.createBucket(AliyunOSSProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}

			int retry = 0;
			while (!result && (retry++ < AliyunOSSProvider.RETRY_TIMES)) {
				try {
					result = AliyunOSSProvider.adapter
							.isBucketExist(AliyunOSSProvider.BUCKET_NAME);
					if (result) {
						isReady = true;
						break;
					}
				} catch (Exception e) {
					AliyunOSSProvider.logger.warn(getName()
							+ " query bucket failed", e);
				}

				AliyunOSSProvider.BUCKET_NAME = UUID.randomUUID().toString();
				config.setBucketName(AliyunOSSProvider.BUCKET_NAME);
				AliyunOSSProvider.adapter
						.createBucket(AliyunOSSProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " init failed", e);
			result = false;
		}
		return result;
	}

	@Override
	public void setProviderStatistics(ProviderStatistics stat) {
		statistics = stat;
	}
}
